// simulate_code.cpp
#include <bits/stdc++.h>

using namespace std;

const int infi=255;
const int tot=2000;

struct edge{
	int to,next;
}e[tot*9+1];

bool link[tot+1][tot+1];
int box[tot+1];
int node_num,edge_num;

int f[tot+1][tot+1];
vector<vector<vector<unsigned char>>> dist;

void addedge(int s,int t){
	link[s][t]=true;
	edge_num++;
	e[edge_num].to=t;
	e[edge_num].next=box[s];
	box[s]=edge_num;
}

void make_graph(){
    freopen("adj_matrix.txt","r",stdin);
	for (int i=1;i<=tot;i++){
		for (int j=1;j<=tot;j++)
			link[i][j]=false;
		box[i]=0;
	}
	scanf("%d",&node_num);
	edge_num=0;
	int max_cnt=0,max_f=0;
	for (int i=1;i<=node_num;i++){
		int cnt=0;
		for (int j=1;j<=node_num;j++){
			int x;
			scanf("%d",&x);
			if (x==1){
				addedge(i,j);
				if (i==j) f[i][j]=0;
				else f[i][j]=1;
				cnt++;
			}
			else f[i][j]=infi;
		}
		max_cnt=max(max_cnt,cnt);
	}
	for (int k=1;k<=node_num;k++)
		for (int i=1;i<=node_num;i++)
			for (int j=1;j<=node_num;j++)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	for (int i=1;i<=node_num;i++)
		for (int j=1;j<=node_num;j++)
			max_f=max(max_f,f[i][j]);
	printf("%d %d\n",max_cnt,max_f);
}

void init(){
	// Read binary data from file
	FILE* infile = fopen("D_bin.txt", "rb");
	if (!infile) {
		printf("Error: Cannot open D_bin.txt for reading\n");
		return;
	}
	
	for (int i=0;i<=node_num;i++){
		vector<vector<unsigned char>> l1;
		for (int j=0;j<=node_num;j++){
			vector<unsigned char> l2;
			for (int l=0;l<=node_num;l++){
				int ch = fgetc(infile);
				if (ch == EOF) ch = infi;  // Handle EOF
				l2.push_back((unsigned char)ch);
			}
			l1.push_back(l2);
		}
		dist.push_back(l1);
	}
	fclose(infile);
}

bool cond1(int i,int j,int l){return link[i][l]||link[j][l];}
void test(){
	srand(time(NULL));
    char temp[]="0.txt";
	for (int T=0;T<10;T++){
        temp[0]=(char)(T+'0');
        FILE* outfile = fopen(temp, "w");
        if (!outfile) continue;
        
        int l=rand()%node_num+1,i=rand()%node_num+1,j=rand()%node_num+1,cnt=0;
        while (cnt++<200){
            fprintf(outfile, "%d %d %d %d\n",dist[i][j][l],l,i,j);
            if (cond1(i,j,l)) break;
            int a[3],ans_e=0,ans_p=infi;
			int d_pursuer=infi,d_evader=0;
			a[0]=l,a[1]=i,a[2]=j;
			for (int t=box[l];t;t=e[t].next){
				int mini=infi;
				for (int x=box[i];x;x=e[x].next)
					for (int y=box[j];y;y=e[y].next)
						if (dist[e[x].to][e[y].to][e[t].to]<mini)
							mini=dist[e[x].to][e[y].to][e[t].to];
				if (mini>ans_e||mini==ans_e&&f[i][e[t].to]+f[j][e[t].to]>d_evader){
					ans_e=mini;
					d_evader=f[i][e[t].to]+f[j][e[t].to];
					a[0]=e[t].to;
				}
            }
			for (int x=box[i];x;x=e[x].next)
				for (int y=box[j];y;y=e[y].next){
					int maxi=-1,temp;
					for (int t=box[l];t;t=e[t].next)
						if (dist[e[x].to][e[y].to][e[t].to]>maxi){
							maxi=dist[e[x].to][e[y].to][e[t].to];
							temp=e[t].to;
						}
					if (maxi<ans_p||maxi==ans_p&&f[e[x].to][l]+f[e[y].to][l]<d_pursuer){
						ans_p=maxi;
						d_pursuer=f[e[x].to][l]+f[e[y].to][l];
						a[1]=e[x].to,a[2]=e[y].to;
					}
                }
            l=a[0],i=a[1],j=a[2];
		}
		fclose(outfile);
	}
}

int main(){
	make_graph();
	init();
	test();
	return 0;
}
